home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1993 July / InfoMagic USENET CD-ROM July 1993.ISO / sources / unix / volume25 / zip / part04 < prev    next >
Encoding:
Text File  |  1992-03-01  |  78.7 KB  |  2,204 lines

  1. Newsgroups: comp.sources.unix
  2. From: madler@cco.caltech.edu (Mark Adler)
  3. Subject: v25i145: zip - file compression/archive tool, Part04/07
  4. Sender: unix-sources-moderator@pa.dec.com
  5. Approved: vixie@pa.dec.com
  6.  
  7. Submitted-By: madler@cco.caltech.edu (Mark Adler)
  8. Posting-Number: Volume 25, Issue 145
  9. Archive-Name: zip/part04
  10.  
  11. #! /bin/sh
  12. # This is a shell archive.  Remove anything before this line, then unpack
  13. # it by saving it into a file and typing "sh file".  To overwrite existing
  14. # files, type "sh file -c".  You can also feed this as standard input via
  15. # unshar, or by typing "sh <file", e.g..  If this archive is complete, you
  16. # will see the following message at the end:
  17. #        "End of archive 4 (of 7)."
  18. # Contents:  im_lmat.c zip.c zip.doc
  19. # Wrapped by vixie@cognition.pa.dec.com on Sun Mar  1 18:57:38 1992
  20. PATH=/bin:/usr/bin:/usr/ucb ; export PATH
  21. if test -f 'im_lmat.c' -a "${1}" != "-c" ; then 
  22.   echo shar: Will not clobber existing file \"'im_lmat.c'\"
  23. else
  24. echo shar: Extracting \"'im_lmat.c'\" \(27597 characters\)
  25. sed "s/^X//" >'im_lmat.c' <<'END_OF_FILE'
  26. X/*
  27. X
  28. X Copyright (C) 1990,1991 Mark Adler, Richard B. Wales, and Jean-loup Gailly.
  29. X Permission is granted to any individual or institution to use, copy, or
  30. X redistribute this software so long as all of the original files are included
  31. X unmodified, that it is not sold for profit, and that this copyright notice
  32. X is retained.
  33. X
  34. X*/
  35. X
  36. X/*
  37. X *  im_lmat.c by Jean-loup Gailly.
  38. X *
  39. X *  PURPOSE
  40. X *
  41. X *      Identify new text as repetitions of old text within a fixed-
  42. X *      length sliding window trailing behind the new text.
  43. X *
  44. X *  DISCUSSION
  45. X *
  46. X *      The "implosion" process depends on being able to identify portions
  47. X *      of the input text which are identical to earlier input (within a
  48. X *      sliding window trailing behind the input currently being processed).
  49. X *
  50. X *      The most straightforward technique turns out to be the fastest for
  51. X *      most input files: try all possible matches and select the longest.
  52. X *      The key feature is of this algorithm is that insertion and deletions
  53. X *      from the string dictionary are very simple and thus fast. Insertions
  54. X *      and deletions are performed at each input character, whereas string
  55. X *      matches are performed only when the previous match ends. So it is
  56. X *      preferable to spend more time in matches to allow very fast string
  57. X *      insertions and deletions. The matching algorithm for small strings
  58. X *      is inspired from that of Rabin & Karp. A brute force approach is
  59. X *      used to find longer strings when a small match has been found.
  60. X *      A similar algorithm is used in freeze (by Leonid Broukhis) but the
  61. X *      algorithm used here is faster.
  62. X *         A previous version of this file used a more sophisticated algorithm
  63. X *      (by Fiala and Greene) which is guaranteed to run in linear amortized
  64. X *      time, but has a larger average cost and uses more memory. However
  65. X *      the F&G algorithm may be faster for some highly redundant files if
  66. X *      the parameter max_chain_length (described below) is too large.
  67. X *
  68. X *  ACKNOWLEDGEMENTS
  69. X *
  70. X *      Rich Wales defined the interface, provided the necessary information
  71. X *      to ensure compatibility with pkunzip 1.0 (not an easy job) and
  72. X *      suggested the solution (n == 1 + n-1) adopted here.
  73. X *      The idea of lazy evaluation of matches is due to Jan Mark Wams, and
  74. X *      I found it in 'freeze' written by Leonid Broukhis.
  75. X *      Special thanks to Kai-Uwe Rommel for the OS/2 port, to Glenn J.
  76. X *      Andrews for the VMS port, and to many other info-zippers for testing.
  77. X *
  78. X *  REFERENCES
  79. X *
  80. X *      A description of the Rabin and Karp algorithm is given in the book
  81. X *          "Algorithms" by R. Sedgewick, Addison-Wesley, p252.
  82. X *
  83. X *      Fiala,E.R., and Greene,D.H.
  84. X *          Data Compression with Finite Windows, CACM, 32,4 (1989) 490-595.
  85. X *
  86. X *  INTERFACE
  87. X *
  88. X *      ImpErr lm_init (int pack_level)
  89. X *          Initialize the "longest match" routines for a new file.
  90. X *          The global variable fd is an implicit parameter.
  91. X *
  92. X *      ImpErr lm_input (U_CHAR *block, U_INT count)
  93. X *          Process a block of input characters.
  94. X *
  95. X *      ImpErr lm_windup (void)
  96. X *          Flush out the remaining unprocessed input.
  97. X */
  98. X
  99. X#include "implode.h"
  100. X
  101. X/***********************************************************************
  102. X *
  103. X * Configuration parameters
  104. X */
  105. X
  106. X#define MAX_MATCH_LENGTH  320
  107. X/* The maximum match length. 320 = 64 + 256. (If the length is greater than
  108. X * 63, pkzip uses an extra byte.)
  109. X */
  110. X
  111. X#define MAX_WBITS  13
  112. X#define WSIZE (1 << MAX_WBITS)
  113. X/* Maximum window size = 8K */
  114. X
  115. X/* Constants used to dimension the hash table: */
  116. X#define HASH_BITS  14
  117. X/* HASH_BITS must be >= 13, see longest_match() */
  118. X
  119. X#define HASH_SIZE (1<<HASH_BITS)
  120. X#define HASH_MASK (HASH_SIZE-1)
  121. X
  122. X#if defined(MSDOS) || defined(i386) || defined(mc68020) || defined(vax)
  123. X#   define UNALIGNED_OK
  124. X    /* Define this symbol if your target allows access to unaligned data.
  125. X     * This is not mandatory, just a speed optimization. The compressed
  126. X     * output is strictly identical.
  127. X     */
  128. X#endif
  129. X#ifdef __TURBOC__
  130. X#   define DYN_ALLOC
  131. X    /* Turbo C 2.0 does not accept far static allocations in small model */
  132. X#endif
  133. X
  134. X/***********************************************************************
  135. X *
  136. X * Local data used by the "longest match" routines.
  137. X */
  138. X
  139. X#if HASH_BITS <= 14
  140. X   typedef unsigned short Hash;
  141. X#else
  142. X   /* Defined just for safety, since values > 14 do not speed up implosion */
  143. X   typedef unsigned long Hash;
  144. X#endif
  145. X
  146. typedef unsigned short Pos;
  147. typedef unsigned int  IPos;
  148. X/* A Pos is an index in the character window. We use short instead of int to
  149. X * save space in the various tables. IPos is used only for parameter passing.
  150. X */
  151. X
  152. int near min_match_length;
  153. X/* Minimum match length, 2 for binary files, 3 for ascii files.
  154. X * (bad luck for ebcdic users; not because they may not get optimal
  155. X * compression, but because they have to use ebcdic machines :-)
  156. X * A zero value means that the min_match_length is not yet determined.
  157. X */
  158. X
  159. U_CHAR near window[MAX_MATCH_LENGTH + WSIZE + BSZ];
  160. X/* MAX_MATCH_LENGTH bytes are duplicated at both ends of the window,
  161. X * to speed up string comparisons. The BSZ extra bytes allow a block copy
  162. X * of the input buffer into the window instead of a copy one byte at a time.
  163. X */
  164. X
  165. X#define MAX_DIST (WSIZE + BSZ)
  166. X/* Maximum theoretical distance between two distinct bytes in the window.
  167. X * Actual distances are limited to bufsize.
  168. X */
  169. X
  170. X#define NIL  MAX_DIST
  171. X/* Tail of hash chains */
  172. X
  173. X#ifdef DYN_ALLOC
  174. X   Hash far *next = NULL;
  175. X   Pos  far *prev = NULL;
  176. X#else
  177. X   Hash far next[MAX_DIST+1];
  178. X   Pos  far prev[MAX_DIST+HASH_SIZE+1];
  179. X#endif
  180. X/* next is a link to a more recent string with same hash index, or to the head
  181. X * of a hash table chain if there is no such string. next[NIL] is used to
  182. X * avoid extra checks. next[s] is NIL if string s is not yet in the dictionary
  183. X *
  184. X * prev is a link to an older string with same hash index (first MAX_DIST
  185. X * values) or head of hash chain (last HASH_SIZE values). prev[NIL] is used
  186. X * to avoid extra checks.
  187. X */
  188. X#define match_head (prev+(MAX_DIST+1))
  189. X
  190. Hash near ins_h;  /* hash index of string to be inserted. */
  191. X
  192. int near h_shift;
  193. X/* Number of bits by which ins_h must be shifted at each
  194. X * input step. It must be such that after min_match_length steps, the oldest
  195. X * byte no longer takes part in the hash key, that is:
  196. X *   h_shift * min_match_length >= HASH_BITS
  197. X */
  198. X
  199. MATCH *ma_buf = NULL;
  200. X/* Buffer used to speed up reading/writing to/from temp file */
  201. X#define MA_BUFEND (ma_buf+MA_BUFSIZE)
  202. X
  203. MATCH *ma;
  204. X/* Pointer to the most recent match. */
  205. X
  206. int near start_length;
  207. X/* Matches not greater than this are discarded. This is used in the lazy match
  208. X * evaluation. If start_length > 1, ma is a valid guess of length start_length
  209. X * and ct_tally has not yet been called.
  210. X */
  211. X
  212. X        int near strstart;      /* start of string to insert */
  213. X        int near strsize;       /* length of string to insert */
  214. X        int near match_length;  /* length of current best match */
  215. X        int near bufsize;       /* # of slots in window */
  216. X        int near checkpoint;    /* look for new match at this point */
  217. static  int      insert_point;  /* position of next input buffer */
  218. X
  219. static  int      max_lazy_match;
  220. X/* We try lazy evaluation only for matches of length 2..max_lazy_match, to
  221. X * speed up the implosion. We use 0 for maximum speed, 0.9*MAX_MATCH_LENGTH
  222. X * for maximum compression.
  223. X */
  224. X
  225. X        int near max_chain_length;
  226. X/* To speed up implosion, hash chains are truncated to this length.
  227. X * A higher limit improves compression ratio but degrades the speed.
  228. X * We use 40 for maximum speed, 960 for maximum compression. Values
  229. X * below 20 are not recommended.
  230. X */
  231. X
  232. X/* Values for max_lazy_match and max_chain_length, depending on the desired
  233. X * pack level (0..9). The values given below have been tuned to exclude
  234. X * worst case performance for pathological files. Better values may be
  235. X * found for specific files. Note that the current algorithm requires 
  236. X * max_lazy >= 2.
  237. X */
  238. typedef struct config {
  239. X   int max_lazy;
  240. X   int max_chain;
  241. X} config;
  242. X
  243. static config configuration_table[10] = {
  244. X/* 0 */ {2,                     MAX_MATCH_LENGTH/8}, /* maximum speed */
  245. X/* 1 */ {4,                     MAX_MATCH_LENGTH/4},
  246. X/* 2 */ {5,                     MAX_MATCH_LENGTH/2},
  247. X/* 3 */ {MAX_MATCH_LENGTH/16,   MAX_MATCH_LENGTH/2},
  248. X/* 4 */ {MAX_MATCH_LENGTH/16,   3*MAX_MATCH_LENGTH/4},
  249. X/* 5 */ {MAX_MATCH_LENGTH/16,   MAX_MATCH_LENGTH},
  250. X/* 6 */ {MAX_MATCH_LENGTH/16,   3*MAX_MATCH_LENGTH/2},
  251. X/* 7 */ {MAX_MATCH_LENGTH/16,   2*MAX_MATCH_LENGTH},
  252. X/* 8 */ {9*MAX_MATCH_LENGTH/10, 2*MAX_MATCH_LENGTH},
  253. X/* 9 */ {9*MAX_MATCH_LENGTH/10, 3*MAX_MATCH_LENGTH}}; /* maximum compression */
  254. X
  255. X
  256. X#define MIN(a,b) ((a) <= (b) ? (a) : (b))
  257. X/* The arguments must not have side effects. */
  258. X
  259. X#define EQUAL 0
  260. X/* result of strncmp for equal strings */
  261. X
  262. X/*  Prototypes for local functions */
  263. X
  264. static void   set_min_match_length OF ((U_CHAR *block, U_INT count));
  265. X       ImpErr write_match OF ((IPos ma_start, int ma_length));
  266. X       IPos   longest_match OF ((IPos cur_match));
  267. X       ImpErr lm_process OF ((U_INT count));
  268. X
  269. X/***********************************************************************
  270. X *
  271. X * Initialize the "longest match" routines for a new file.
  272. X * The global variable fd is an implicit parameter.
  273. X */
  274. ImpErr lm_init (pack_level)
  275. X    int pack_level; /* 0: best speed, 9: best compression, other: default */
  276. X{
  277. X    register int i;
  278. X
  279. X    /* Validate the arguments */
  280. X    bufsize = fd.fd_bufsize;
  281. X    strsize = MIN (fd.fd_strsize, MAX_MATCH_LENGTH);
  282. X    if (bufsize > WSIZE)          return IM_BADARG;
  283. X    if (bufsize < 2 * strsize)    return IM_BADARG;
  284. X    if (pack_level < 0 || pack_level > 9) return IM_BADARG;
  285. X
  286. X    /* Make sure "bufsize" is a power of 2 */
  287. X    if ((bufsize & (bufsize - 1)) != 0) return IM_BADARG;
  288. X
  289. X    /* Use dynamic allocation if compiler does not like big static arrays: */
  290. X#ifdef DYN_ALLOC
  291. X    if (prev == NULL) {
  292. X       next = (Hash far*)farmalloc((U_INT)(MAX_DIST+9)*sizeof(Hash));
  293. X       prev = (Pos far*) farmalloc((U_INT)(MAX_DIST+HASH_SIZE+9)*sizeof(Pos));
  294. X       /* We allocate 16 extra bytes for the normalization under MSDOS */
  295. X       if (prev == NULL || next == NULL) return IM_NOMEM;
  296. X
  297. X#   if defined(MSDOS) && !defined(OS2)
  298. X       /* Normalize to pointers with offset 0 (required by asm version).
  299. X        * For OS/2, we can't of course play such nasty games.
  300. X        */
  301. X#define NORMALIZE(ptr) { \
  302. X   *((int*)&ptr+1) += ((unsigned)(ptr-0) + 15) >> 4; \
  303. X   *(int*)&ptr = 0; \
  304. X}
  305. X       NORMALIZE(prev); NORMALIZE(next);
  306. X#   endif
  307. X    }
  308. X#endif /* DYN_ALLOC */
  309. X
  310. X    /* Initialize the hash tables. */
  311. X    for (i = 0;  i < HASH_SIZE; i++) match_head[i] = NIL;
  312. X    for (i = 0;  i <= MAX_DIST; i++) next[i] = NIL;
  313. X    /* prev[0..MAX_DIST] will be initialized on the fly */
  314. X    ins_h = 0;
  315. X
  316. X    /* Assume strsize zeros before the input (bytes beyond strsize
  317. X     * can be garbage):
  318. X     */
  319. X    memset((char*)window, 0, MAX_MATCH_LENGTH);
  320. X    /* It is not necessary to duplicate this at the end of the window.
  321. X     * Duplication will start only after the first wrap around.
  322. X     */
  323. X    insert_point = MAX_MATCH_LENGTH;
  324. X
  325. X    /* Force a check for the file type (ascii/binary) and set the default
  326. X     * configuration parameters:
  327. X     */
  328. X    min_match_length = 0;
  329. X    max_lazy_match   = configuration_table[pack_level].max_lazy;
  330. X    max_chain_length = configuration_table[pack_level].max_chain;
  331. X
  332. X    /* Do not report matches before the first strsize strings have been
  333. X     * inserted in the suffix tree:
  334. X     */
  335. X    strstart = 0;
  336. X    checkpoint = strsize;
  337. X    if (ma_buf == NULL) {
  338. X        ma_buf = (MATCH *) malloc ((unsigned) (MA_BUFSIZE * sizeof (MATCH)));
  339. X        if (ma_buf == NULL) return IM_NOMEM;
  340. X    }
  341. X    ma = ma_buf - 1;
  342. X    start_length = 1;
  343. X
  344. X    /* All done. */
  345. X    return IM_OK;
  346. X}
  347. X
  348. X/***********************************************************************
  349. X *
  350. X * Output the match info.
  351. X * IN assertions: The matching strings start at strstart and ma_start
  352. X *    and have a length of ma_length bytes.
  353. X *    If ma_length is not greater than start_length, ma_start is garbage.
  354. X *    strstat == checkpoint. If start_length > 1, ma is the
  355. X *    previous match which has not yet been output.
  356. X * OUT assertion: checkpoint is reset according to the match length
  357. X *    actually chosen.
  358. X *    ma is set to the current match, with start_length set appropriately.
  359. X */
  360. ImpErr write_match(ma_start, ma_length)
  361. X    IPos ma_start;           /* start of matched string */
  362. X    int ma_length;           /* length of complete match */
  363. X{
  364. X    int ma_dist = 0;         /* distance of current match */
  365. X
  366. X    /* ma_length can be too large towards the end of the input: */
  367. X    if (ma_length > strsize) ma_length = strsize;
  368. X
  369. X#ifdef DEBUG
  370. X    /* check that the match is indeed a match */
  371. X    if (ma_length > start_length &&
  372. X        strncmp(window + ma_start, window + strstart, ma_length) != EQUAL) {
  373. X        fprintf(stderr,
  374. X            "write_match: ma_start %d, strstart %d, ma_length %d\n",
  375. X            ma_start, strstart, ma_length);
  376. X        exit(1);
  377. X    }
  378. X#endif
  379. X    /* PKUNZIP accepts most overlapping matches.  However, when the
  380. X     * distance has the value 1, versions of PKUNZIP prior to 1.10 don't
  381. X     * handle the overlap properly -- and version 1.10 handles the
  382. X     * overlap correctly only if the length is limited to 62 plus the
  383. X     * minimum match length; i.e., only if there is no supplementary
  384. X     * length byte.  (From phone conversation with Phil Katz, 23 January
  385. X     * 1991.) The compression ratio is generally better when we do not
  386. X     * limit the match length to 64, so we remove distance-one matches
  387. X     * completely. (But PKUNZIP 1.01 also rejects some distance-two matches.
  388. X     * This could be fixed but would degrade compression.)
  389. X     */
  390. X    if (ma_length > 1) {
  391. X        ma_dist = strstart - ma_start;
  392. X        if (ma_dist < 0) ma_dist += MAX_DIST;
  393. X        if (ma_dist == 1) {
  394. X            /* keep the previous match if it was delayed */
  395. X            if (start_length > 1) {
  396. X                ma_length = 1;
  397. X            } else {
  398. X                /* Truncate the match to 1 */
  399. X                ImpErr retcode = write_match(ma_start, 1);
  400. X                if (retcode != IM_OK) return retcode;
  401. X
  402. X                /* Emit a match with a distance of two and a length reduced by
  403. X                 * one. This reduced match may be delayed.
  404. X                 */
  405. X                checkpoint = ++strstart;
  406. X                retcode = write_match(ma_start, ma_length-1);
  407. X                strstart--;
  408. X                return retcode; /* Leave checkpoint unchanged */
  409. X            } /* start_length > 1 */
  410. X        } /* ma_dist == 1 */
  411. X    } /* ma_length > 1 */
  412. X
  413. X    /* If the previous match has been delayed, keep it or prefer the
  414. X     * current match:
  415. X     */
  416. X    if (start_length > 1) {
  417. X        /* Keep the previous match if it is not shorter than the current one.
  418. X         * Otherwise, emit only the first byte of the previous match,
  419. X         * followed by the current match. If we have a delayed match for
  420. X         * the last bytes of the input file, the next match will necessarily
  421. X         * be smaller, so ct_tally will correctly be called for the delayed
  422. X         * match.
  423. X         */
  424. X        if (start_length >= ma_length) {
  425. X            /* Keep the previous match */
  426. X            if (start_length == 2) {
  427. X                ma->ma_dist = - ma->ma_dist;
  428. X                ma->l.ma_litc[1] = window[strstart]; /* litc[0] already set */
  429. X            } else {
  430. X                ma->l.ma_length = start_length; /* overwrite ma->l.ma_litc */
  431. X            }
  432. X            checkpoint = strstart + start_length - 1;
  433. X            start_length = 1;
  434. X            return ct_tally (ma);
  435. X        }
  436. X        /* Shorten the previous match to zero */
  437. X        ma->ma_dist = 0; /* keep ma->l.ma_litc */
  438. X        start_length = 1;
  439. X        (void) ct_tally (ma); /* ignore result, ct_tally cannot fail */
  440. X    }
  441. X
  442. X    if (++ma == MA_BUFEND) {
  443. X        ma = ma_buf;
  444. X        if (twrite ((char *) ma, sizeof(MATCH), MA_BUFSIZE, fd.fd_temp)
  445. X            != MA_BUFSIZE) return IM_IOERR;
  446. X    }
  447. X
  448. X    /* Keep the current match as guess only if its length is small,
  449. X     * trying to find a better match at the next step. If speed is not
  450. X     * critical, we use this lazy mechanism for all lengths.
  451. X     */
  452. X    if (ma_length > 1) {
  453. X        ma->ma_dist = ma_dist;
  454. X        if (ma_length <= max_lazy_match) {
  455. X           /* Set ma_litc[0]: this is the only way to identify the unmatched
  456. X            * data if the delayed match will be truncated to 1. It is also
  457. X            * useful if ma_length == 2: it may be more efficient in this case
  458. X            * to encode the individual characters rather than the match info.
  459. X            */
  460. X            ma->l.ma_litc[0] = window[strstart];
  461. X            start_length = ma_length;
  462. X            checkpoint = strstart + 1;
  463. X            return IM_OK;
  464. X        }
  465. X        /* At this point, ma_length >= 3, no need for ma_litc */
  466. X        ma->l.ma_length = ma_length;
  467. X        checkpoint = strstart + ma_length;
  468. X    } else {
  469. X        ma->ma_dist = 0;
  470. X        ma->l.ma_litc[0] = window[strstart]; /* ma_litc[1] is not required */
  471. X        checkpoint = strstart + 1;
  472. X    }
  473. X    return ct_tally (ma);
  474. X    /* Keep start_length == 1 */
  475. X}
  476. X
  477. X/***********************************************************************
  478. X *
  479. X * Determine the minimum match length, based on the type of data
  480. X * in the given input buffer: 2 for binary data, 3 otherwise. Set also
  481. X * h_shift according to the chosen min_match_length, and reduce
  482. X * max_chain_length for binary files.
  483. X *    If the guess about data type is wrong, this only affects the
  484. X * compression ratio and speed but not the correctness of the algorithms.
  485. X * If there are more than 20% bytes which seem non ascii in the first
  486. X * 500 bytes, we assume that the data is binary. (We accept data
  487. X * with a few high bits set as ascii to take into account special
  488. X * word processor formats.)
  489. X */
  490. static void set_min_match_length (block, count)
  491. X    U_CHAR *block;          /* input data */
  492. X    U_INT  count;           /* # of input char's */
  493. X{
  494. X    int non_ascii = 0;
  495. X    min_match_length = 3;  /* Default ascii */
  496. X    if (count >= 500) {
  497. X        count = 500;
  498. X        while (--count != 0) {
  499. X            if (*block <= 6 || *block >= 0x80) non_ascii++;
  500. X            block++;
  501. X        }
  502. X        if (non_ascii > 100) {
  503. X            min_match_length = 2;
  504. X            max_chain_length >>= 2;
  505. X        }
  506. X    }
  507. X    h_shift = (HASH_BITS+min_match_length-1)/min_match_length;
  508. X#ifdef DEBUG
  509. X    fprintf(stderr," (min_match_length %d) ", min_match_length);
  510. X#endif
  511. X}
  512. X
  513. X/***********************************************************************
  514. X *
  515. X * Insert string s in the dictionary and set last_match to the previous head
  516. X * of the hash chain (the most recent string with same hash key).
  517. X * IN  assertion: all calls to to INSERT_STRING are made with consecutive
  518. X *    input characters, so that a running hash key can be computed from the
  519. X *    previous key instead of complete recalculation each time.
  520. X */
  521. X#define INSERT_STRING(s, last_match) \
  522. X{ \
  523. X    ins_h = ((ins_h<<h_shift) ^ window[s + min_match_length-1]) & HASH_MASK; \
  524. X    prev[s] = last_match = match_head[ins_h]; \
  525. X    next[last_match] = prev[next[s] = ins_h + MAX_DIST+1] = s; \
  526. X}
  527. X    /* next[NIL] is garbage, we can overwrite it if s is a tail */
  528. X
  529. X/***********************************************************************
  530. X *
  531. X * Remove string s from the dictionary, or do nothing if s is not yet
  532. X * in the dictionary.
  533. X * IN assertion: s is the tail of its hash chain (the oldest string).
  534. X */
  535. X#define DELETE_STRING(s)  {prev[next[s]] = NIL;}
  536. X/* No effect if next[s] == NIL (s not in dictionary) */
  537. X
  538. X/***********************************************************************
  539. X *
  540. X * Find the longest match starting at the given string. Return its position
  541. X * and set its length in match_length. Matches shorter or equal to
  542. X * start_length are discarded, in which case match_length is unchanged
  543. X * and the result position is NIL.
  544. X * IN assertions: cur_match is the head of the hash chain for the current
  545. X *   string (strstart) and is not NIL, and start_length >= 1
  546. X */
  547. X#if !defined(MSDOS) || defined(NO_ASM)
  548. X/* For MSDOS, a version of this routine written in assembler is in im_lm.asm.
  549. X * The algorithms are strictly equivalent, so the C version can be used
  550. X * instead if you do not have masm or tasm. (Update the makefile in this case.)
  551. X */
  552. IPos longest_match(cur_match)
  553. X    IPos cur_match;
  554. X{
  555. X    register U_CHAR *match;                   /* pointer in matched string */
  556. X    register U_CHAR *scan = window + strstart;/* pointer in current string */
  557. X    register int len;                         /* length of current match */
  558. X    IPos cur_best = NIL;                      /* best match so far */
  559. X    register int ma_length = start_length;    /* best match length so far */
  560. X    int chain_count = max_chain_length;       /* used to limit hash chains */
  561. X    typedef unsigned short US;
  562. X    typedef unsigned long  UL;
  563. X#ifdef UNALIGNED_OK
  564. X    register US scan_start = *(US*)scan;
  565. X    register US scan_end   = *(US*)(scan+ma_length-1);
  566. X#else
  567. X    register U_CHAR scan_start = *scan;
  568. X    register U_CHAR scan_end1  = scan[ma_length-1];
  569. X    register U_CHAR scan_end   = scan[ma_length];
  570. X#endif
  571. X    do {
  572. X        match = window + cur_match;
  573. X        /* Skip to next match if the match length cannot increase
  574. X         * or if the match length is less than 2:
  575. X         */
  576. X#ifdef UNALIGNED_OK
  577. X        /* This code assumes sizeof(unsigned short) == 2 and
  578. X         * sizeof(unsigned long) == 4. Do not use UNALIGNED_OK if your
  579. X         * compiler uses different sizes.
  580. X         */
  581. X        if (*(US*)(match+ma_length-1) != scan_end ||
  582. X            *(US*)match != scan_start) continue;
  583. X
  584. X        len = min_match_length - 4;
  585. X        /* If min_match_length == 3, it is not necessary to compare
  586. X         * scan[2] and match[2] since they are always equal when the other
  587. X         * bytes match, given that the hash keys are equal and that
  588. X         * HASH_BITS >= 8.
  589. X         */
  590. X#       define ML MAX_MATCH_LENGTH
  591. X        do {} while ((len+=4) < ML && *(UL*)(scan+len) == *(UL*)(match+len));
  592. X
  593. X        if (*(US*)(scan+len) == *(US*)(match+len)) len += 2;
  594. X        if (scan[len] == match[len]) len++;
  595. X
  596. X#else /* UNALIGNED_OK */
  597. X        if (match[ma_length] != scan_end ||
  598. X            match[ma_length-1] != scan_end1 || *match != scan_start)
  599. X           continue;
  600. X        /* It is not necessary to compare scan[1] and match[1] since they
  601. X         * are always equal when the other bytes match, given that
  602. X         * the hash keys are equal and that h_shift+8 <= HASH_BITS,
  603. X         * that is, when the last byte is entirely included in the hash key.
  604. X         * The condition is equivalent to
  605. X         *       (HASH_BITS+2)/3 + 8 <= HASH_BITS
  606. X         * or: HASH_BITS >= 13 (see set_min_match_length()).
  607. X         * Also, we check for a match at ma_length-1 to get rid quickly of
  608. X         * the match with the suffix of the match made at the previous step,
  609. X         * which is known to fail.
  610. X         */
  611. X        len = 1;
  612. X        do {} while (++len < MAX_MATCH_LENGTH && scan[len] == match[len]);
  613. X
  614. X#endif /* UNALIGNED_OK */
  615. X
  616. X        if (len > ma_length) {
  617. X            cur_best = cur_match, ma_length = len;
  618. X            if (len >= strsize) break;
  619. X#ifdef UNALIGNED_OK
  620. X            scan_end = *(US*)(scan+ma_length-1);
  621. X#else
  622. X            scan_end1  = scan[ma_length-1];
  623. X            scan_end   = scan[ma_length];
  624. X#endif
  625. X        }
  626. X    } while (--chain_count != 0 && (cur_match = prev[cur_match]) != NIL);
  627. X
  628. X    if (ma_length > start_length) match_length = ma_length;
  629. X    return cur_best;
  630. X}
  631. X#endif /* MSDOS */
  632. X
  633. X/***********************************************************************
  634. X *
  635. X * Process a block of input characters, generating zero or more match
  636. X * info records as appropriate.
  637. X * IN assertion: count <= BSZ
  638. X */
  639. ImpErr lm_input (block, count)
  640. X    U_CHAR *block;          /* input data */
  641. X    U_INT  count;           /* # of input char's */
  642. X{
  643. X    if (count == 0) return IM_OK;
  644. X
  645. X    /* Determine the input file type if this is the first call */
  646. X    if (min_match_length == 0) set_min_match_length (block, count);
  647. X
  648. X    if (insert_point + count <= sizeof(window)) {
  649. X        memcpy((char*)window + insert_point, (char*)block, count);
  650. X
  651. X    } else {
  652. X        int remain = sizeof(window)-insert_point;
  653. X        memcpy((char*)window + insert_point, (char*)block, remain);
  654. X
  655. X        memcpy((char*)window + MAX_MATCH_LENGTH,
  656. X               (char*)block + remain, count - remain);
  657. X    }
  658. X    insert_point += count;
  659. X    if (insert_point > MAX_DIST) {
  660. X        /* Duplicate the end of the window */
  661. X        memcpy((char*)window,
  662. X               (char*)window + MAX_DIST,
  663. X               MIN (insert_point - MAX_DIST, MAX_MATCH_LENGTH));
  664. X    }
  665. X    if (insert_point >= sizeof(window)) insert_point -= MAX_DIST;
  666. X
  667. X    return lm_process(count);
  668. X}
  669. X
  670. X/***********************************************************************
  671. X *
  672. X * Process a block of characters already inserted in the window
  673. X * IN assertion: count > 0
  674. X */
  675. X#if !defined(MSDOS) || defined(NO_ASM)
  676. ImpErr lm_process (count)
  677. X    U_INT  count;           /* number of bytes to process */
  678. X{
  679. X    ImpErr retcode;         /* as usual */
  680. X    IPos cur_match;         /* starting point for longest match search */
  681. X    IPos best_match = NIL;  /* longest match found */
  682. X    int delete_point;       /* position of next string to remove */
  683. X    
  684. X    delete_point = strstart - bufsize + MAX_MATCH_LENGTH - 1;
  685. X    if (delete_point < 0) delete_point += MAX_DIST;
  686. X
  687. X    /* Process the input block. */
  688. X    do {
  689. X        /* Insert the string window[strstart .. strstart+strsize-1] in the
  690. X         * dictionary, and set cur_match to the head of the hash chain:
  691. X         */
  692. X        INSERT_STRING(strstart, cur_match);
  693. X
  694. X        if (strstart == checkpoint) {
  695. X            /* Find the longest match, discarding those <= start_length */
  696. X            match_length = 0;
  697. X            if (cur_match != NIL) {
  698. X                best_match = longest_match (cur_match);
  699. X                /* longest_match updates match_length if longer match found */
  700. X            }
  701. X            retcode = write_match (best_match, match_length);
  702. X            if (retcode != IM_OK) return retcode;
  703. X        }
  704. X
  705. X        /* Remove the oldest string from the dictionary, except if we have not
  706. X         * yet created bufsize dictionary entries. We could avoid this
  707. X         * deletion and check instead for obsolete pointers in
  708. X         * longest_match(), but this would be slower.
  709. X         */
  710. X#if (MAX_DIST & (MAX_DIST-1)) != 0
  711. X        if (++delete_point == MAX_DIST) delete_point = 0;
  712. X#else
  713. X        delete_point = (delete_point + 1) & (MAX_DIST-1);
  714. X#endif
  715. X        DELETE_STRING (delete_point);
  716. X
  717. X        if (++strstart == MAX_DIST) {
  718. X            strstart = 0, checkpoint -= MAX_DIST;
  719. X        }
  720. X    } while (--count != 0);
  721. X    return IM_OK;
  722. X}
  723. X#endif /* MSDOS */
  724. X
  725. X/***********************************************************************
  726. X *
  727. X * Wind up processing by flushing unprocessed input. For normal processing,
  728. X * this routine is called twice (by imp_size then imp_clear) and the
  729. X * second call does nothing. In case of error, this routine is called only
  730. X * by imp_clear().
  731. X */
  732. ImpErr lm_windup()
  733. X{
  734. X    ImpErr retcode;
  735. X    int matches;
  736. X
  737. X    /* Process the remaining input. */
  738. X    while (strsize > 0) {
  739. X       retcode = lm_process (1);
  740. X       if (retcode != IM_OK) return retcode;
  741. X       --strsize;
  742. X    }
  743. X    /* Flush the match buffer. */
  744. X    if ((matches = ma-ma_buf+1) != 0 && matches != 
  745. X        twrite ((char *) ma_buf, sizeof(MATCH), matches, fd.fd_temp)) {
  746. X        return IM_IOERR;
  747. X    }
  748. X    ma = ma_buf - 1;
  749. X    return IM_OK;
  750. X}
  751. END_OF_FILE
  752. if test 27597 -ne `wc -c <'im_lmat.c'`; then
  753.     echo shar: \"'im_lmat.c'\" unpacked with wrong size!
  754. fi
  755. # end of 'im_lmat.c'
  756. fi
  757. if test -f 'zip.c' -a "${1}" != "-c" ; then 
  758.   echo shar: Will not clobber existing file \"'zip.c'\"
  759. else
  760. echo shar: Extracting \"'zip.c'\" \(25825 characters\)
  761. sed "s/^X//" >'zip.c' <<'END_OF_FILE'
  762. X/*
  763. X
  764. X Copyright (C) 1990,1991 Mark Adler, Richard B. Wales, and Jean-loup Gailly.
  765. X Permission is granted to any individual or institution to use, copy, or
  766. X redistribute this software so long as all of the original files are included
  767. X unmodified, that it is not sold for profit, and that this copyright notice
  768. X is retained.
  769. X
  770. X*/
  771. X
  772. X/*
  773. X *  zip.c by Mark Adler.
  774. X */
  775. X
  776. X#include "revision.h"
  777. X#include "zip.h"
  778. X#include <signal.h>
  779. X
  780. X#define PWLEN 80        /* Input buffer size for reading encryption key */
  781. X#define MAXCOM 256      /* Maximum one-line comment size */
  782. X
  783. X
  784. X/* Local option flags */
  785. X#define DELETE  0
  786. X#define ADD     1
  787. X#define UPDATE  2
  788. X#define FRESHEN 3
  789. local int action = ADD; /* one of ADD, UPDATE, FRESHEN, or DELETE */
  790. local int comadd = 0;   /* 1=add comments for new files */
  791. local int zipedit = 0;  /* 1=edit zip comment and all file comments */
  792. local int dispose = 0;  /* 1=remove files after put in zip file */
  793. local int latest = 0;   /* 1=set zip file time to time of latest file */
  794. local ulg before = 0;   /* 0=ignore, else exclude files before this time */
  795. X
  796. X
  797. X/* Temporary zip file name and file pointer */
  798. local char *tempzip;
  799. local FILE *tempzf;
  800. X
  801. X
  802. X/* Local functions */
  803. X#ifdef PROTO
  804. X   local void freeup(void);
  805. X   local void leave(int);
  806. X   local void err(int, char *);
  807. X   local void handler(int);
  808. X   local void license(void);
  809. X   local void help(void);
  810. X   void main(int, char **);
  811. X#endif /* PROTO */
  812. X
  813. X
  814. X
  815. local void freeup()
  816. X/* Free all allocations in the found list and the zfiles list */
  817. X{
  818. X  struct flist far *f;  /* steps through found list */
  819. X  struct zlist far *z;  /* pointer to next entry in zfiles list */
  820. X
  821. X  for (f = found; f != NULL; f = fexpel(f))
  822. X    ;
  823. X  while (zfiles != NULL)
  824. X  {
  825. X    z = zfiles->nxt;
  826. X    free((voidp *)(zfiles->name));
  827. X    free((voidp *)(zfiles->zname));
  828. X    if (zfiles->ext)
  829. X      free((voidp *)(zfiles->extra));
  830. X    if (zfiles->cext && zfiles->cextra != zfiles->extra)
  831. X      free((voidp *)(zfiles->cextra));
  832. X    if (zfiles->com)
  833. X      free((voidp *)(zfiles->comment));
  834. X    farfree((voidp far *)zfiles);
  835. X    zfiles = z;
  836. X    zcount--;
  837. X  }
  838. X}
  839. X
  840. X
  841. local void leave(e)
  842. int e;                  /* exit code */
  843. X/* Process -o and -m options (if specified), free up malloc'ed stuff, and
  844. X   exit with the code e. */
  845. X{
  846. X  int r;                /* return value from trash() */
  847. X  ulg t;                /* latest time in zip file */
  848. X  struct zlist far *z;  /* pointer into zfile list */
  849. X
  850. X  /* If latest, set time to zip file to latest file in zip file */
  851. X  if (latest)
  852. X  {
  853. X    diag("changing time of zip file to time of latest file in it");
  854. X    /* find latest time in zip file */
  855. X    t = zfiles->tim;
  856. X    for (z = zfiles->nxt; z != NULL; z = z->nxt)
  857. X      if (t < z->tim)
  858. X        t = z->tim;
  859. X    /* set modified time of zip file to that time */
  860. X    stamp(zipfile, t);
  861. X  }
  862. X  if (tempath != NULL)
  863. X  {
  864. X    free((voidp *)tempath);
  865. X    tempath = NULL;
  866. X  }
  867. X  if (zipfile != NULL)
  868. X  {
  869. X    free((voidp *)zipfile);
  870. X    zipfile = NULL;
  871. X  }
  872. X
  873. X
  874. X  /* If dispose, delete all files in the zfiles list that are marked */
  875. X  if (dispose)
  876. X  {
  877. X    diag("deleting files that were added to zip file");
  878. X    if ((r = trash()) != ZE_OK)
  879. X      err(r, "was deleting moved files and directories");
  880. X  }
  881. X
  882. X
  883. X  /* Done! */
  884. X  freeup();
  885. X#ifdef VMS
  886. X  exit(0);
  887. X#else /* !VMS */
  888. X  exit(e);
  889. X#endif /* ?VMS */
  890. X}
  891. X
  892. X
  893. local void err(c, h)
  894. int c;                  /* error code from the ZE_ class */
  895. char *h;                /* message about how it happened */
  896. X/* Issue a message for the error, clean up files and memory, and exit. */
  897. X{
  898. X  if (PERR(c))
  899. X    perror("zip error");
  900. X  fprintf(stderr, "zip error: %s (%s)\n", errors[c-1], h);
  901. X  if (shract)
  902. X  {
  903. X    shr_clear();
  904. X    shract = 0;
  905. X  }
  906. X#ifndef NOIMPLODE
  907. X  if (impact)
  908. X  {
  909. X    imp_clear();
  910. X    impact = 0;
  911. X  }
  912. X#endif /* !NOIMPLODE */
  913. X  if (tempzf != NULL)
  914. X    fclose(tempzf);
  915. X  if (tempzip != NULL)
  916. X  {
  917. X    destroy(tempzip);
  918. X    if (tempzip != zipfile)
  919. X      free((voidp *)tempzip);
  920. X  }
  921. X  if (key != NULL)
  922. X    free((voidp *)key);
  923. X  if (tempath != NULL)
  924. X    free((voidp *)tempath);
  925. X  if (zipfile != NULL)
  926. X    free((voidp *)zipfile);
  927. X  freeup();
  928. X#ifdef VMS
  929. X  exit(0);
  930. X#else /* !VMS */
  931. X  exit(c);
  932. X#endif /* ?VMS */
  933. X}
  934. X
  935. X
  936. local void handler(s)
  937. int s;                  /* signal number (ignored) */
  938. X/* Upon getting a user interrupt, turn echo back on for tty and abort
  939. X   cleanly using err(). */
  940. X{
  941. X#ifndef MSVMS
  942. X#ifndef EXPORT
  943. X  echon();
  944. X#endif /* !EXPORT */
  945. X  putc('\n', stderr);
  946. X#endif /* !MSVMS */
  947. X  err(ZE_ABORT, "aborting");
  948. X  s++;                                  /* keep some compilers happy */
  949. X}
  950. X
  951. X
  952. void warn(a, b)
  953. char *a, *b;            /* message strings juxtaposed in output */
  954. X/* Print a warning message to stderr and return. */
  955. X{
  956. X  fprintf(stderr, "zip warning: %s%s\n", a, b);
  957. X}
  958. X
  959. X
  960. local void license()
  961. X/* Print license information to stdout. */
  962. X{
  963. X  extent i;             /* counter for copyright array */
  964. X
  965. X  for (i = 0; i < sizeof(copyright)/sizeof(char *); i++)
  966. X    puts(copyright[i]);
  967. X  for (i = 0; i < sizeof(disclaimer)/sizeof(char *); i++)
  968. X    puts(disclaimer[i]);
  969. X}
  970. X
  971. X
  972. local void help()
  973. X/* Print help (along with license info) to stdout. */
  974. X{
  975. X  extent i;             /* counter for help array */
  976. X
  977. X  /* help array */
  978. X  static char *text[] = {
  979. X"",
  980. X"Zip %d.%d (%s)",
  981. X"Usage:  zip [-options] [-b path] [-t mmddyy] zipfile list [-x list]",
  982. X"  the default action is to add or replace zipfile entries from list, which",
  983. X"  can include the special name - to read names from stdin.",
  984. X"  -f   freshen: only changed files  -u   update: only changed or new files",
  985. X"  -d   delete entries in zipfile    -m   move into zipfile (delete files)",
  986. X"  -k   simulate PKZIP made zipfile  -g   allow growing existing zipfile",
  987. X"  -h   show this help               -l   show software license",
  988. X"  -r   recurse into directories     -j   junk (don't record) directory names",
  989. X"  -i   implode only                 -s   shrink only",
  990. X"  -0   compress faster              -9   compress better",
  991. X"  -q   quiet operation              -n   don't compress special suffixes",
  992. X"  -c   add one-line comments        -z   add zipfile comment",
  993. X"  -b   use \"path\" for temp files    -t   only do files after \"mmddyy\"",
  994. X#ifdef EXPORT
  995. X"  -o   make zipfile as old as latest entry",
  996. X#else /* !EXPORT */
  997. X"  -e   encrypt  (-ee verify key)    -o   make zipfile as old as latest entry",
  998. X#endif /* ?EXPORT */
  999. X#ifdef VMS
  1000. X"  -w   append the VMS version number to the name stored in the zip file",
  1001. X#endif /* VMS */
  1002. X#ifdef S_IFLNK
  1003. X"  -y   store symbolic links as the link instead of the referenced file",
  1004. X#endif /* !S_IFLNK */
  1005. X"  -x   exclude the names that follow from those operated on"
  1006. X  };
  1007. X
  1008. X  for (i = 0; i < sizeof(copyright)/sizeof(char *); i++)
  1009. X    puts(copyright[i]);
  1010. X  for (i = 0; i < sizeof(text)/sizeof(char *); i++)
  1011. X  {
  1012. X    printf(text[i], REVISION / 10, REVISION % 10, REVDATE);
  1013. X    putchar('\n');
  1014. X  }
  1015. X}
  1016. X
  1017. X
  1018. X/* Do command line expansion for MSDOS and VMS */
  1019. X#ifdef MSVMS
  1020. X#  define PROCNAME(n) (action==ADD||action==UPDATE?wild(n):procname(n))
  1021. X#else /* !MSVMS */
  1022. X#  define PROCNAME(n) procname(n)
  1023. X#endif /* ?MSVMS */
  1024. X
  1025. X
  1026. void main(argc, argv)
  1027. int argc;               /* number of tokens in command line */
  1028. char **argv;            /* command line tokens */
  1029. X/* Add, update, freshen, or delete zip entries in a zip file.  See the
  1030. X   command help in help() above. */
  1031. X{
  1032. X  int a;                /* attributes of zip file */
  1033. X  ulg c;                /* start of central directory */
  1034. X  int d;                /* true if just adding to a zip file */
  1035. X  char *e;              /* malloc'd comment buffer */
  1036. X  struct flist far *f;  /* steps through found linked list */
  1037. X  int i;                /* arg counter, root directory flag */
  1038. X  int k;                /* next argument type, marked counter,
  1039. X                           comment size, entry count */
  1040. X  ulg n;                /* total of entry len's */
  1041. X  int o;                /* true if there were any ZE_OPEN errors */
  1042. X  char *p;              /* steps through option arguments */
  1043. X  int r;                /* temporary variable */
  1044. X  ulg t;                /* file time, length of central directory */
  1045. X  struct zlist far *v;  /* temporary variable */
  1046. X  struct zlist far * far *w;    /* pointer to last link in zfiles list */
  1047. X  FILE *x, *y;          /* input and output zip files */
  1048. X  struct zlist far *z;  /* steps through zfiles linked list */
  1049. X
  1050. X
  1051. X  /* Process arguments */
  1052. X  diag("processing arguments");
  1053. X  if (argc == 1)
  1054. X  {
  1055. X    help();
  1056. X    exit(0);
  1057. X  }
  1058. X  zipfile = tempzip = NULL;
  1059. X  tempzf = NULL;
  1060. X  d = 0;                        /* disallow adding to a zip file */
  1061. X  signal(SIGINT, handler);
  1062. X  signal(SIGTERM, handler);
  1063. X  k = 0;                        /* Next non-option argument type */
  1064. X  for (i = 1; i < argc; i++)
  1065. X  {
  1066. X    if (argv[i][0] == '-')
  1067. X      if (argv[i][1])
  1068. X        for (p = argv[i]+1; *p; p++)
  1069. X          switch(*p)
  1070. X          {
  1071. X            case '0':  case '1':  case '2':  case '3':  case '4':
  1072. X            case '5':  case '6':  case '7':  case '8':  case '9':
  1073. X                        /* Set the compression efficacy */
  1074. X              level = *p - '0';  break;
  1075. X            case 'b':   /* Specify path for temporary file */
  1076. X              if (k != 0)
  1077. X                err(ZE_PARMS, "use -b before zip file name");
  1078. X              else
  1079. X                k = 1;          /* Next non-option is path */
  1080. X              break;
  1081. X            case 'c':   /* Add comments for new files in zip file */
  1082. X              comadd = 1;  break;
  1083. X            case 'd':   /* Delete files from zip file */
  1084. X              if (action != ADD)
  1085. X                err(ZE_PARMS, "specify just one action");
  1086. X              action = DELETE;
  1087. X              break;
  1088. X#ifndef EXPORT
  1089. X            case 'e':   /* Encrypt */
  1090. X              e = key == NULL ? (char *)NULL : key;
  1091. X              if ((key = malloc(PWLEN+1)) == NULL)
  1092. X                err(ZE_MEM, "was getting encryption password");
  1093. X              if (getp(e == NULL ? "Enter password: " : "Verify password: ",
  1094. X                       key, PWLEN+1) == NULL)
  1095. X                err(ZE_PARMS, "stderr is not a tty");
  1096. X              if (e != NULL)
  1097. X              {
  1098. X                r = strcmp(key, e);
  1099. X                free((voidp *)e);
  1100. X                if (r)
  1101. X                  err(ZE_PARMS, "password not verified");
  1102. X              }
  1103. X              break;
  1104. X#endif /* !EXPORT */
  1105. X            case 'f':   /* Freshen zip file--overwrite only */
  1106. X              if (action != ADD)
  1107. X                err(ZE_PARMS, "specify just one action");
  1108. X              action = FRESHEN;
  1109. X              break;
  1110. X            case 'g':   /* Allow appending to a zip file */
  1111. X              d = 1;  break;
  1112. X            case 'h':   /* Help */
  1113. X              help();  break;
  1114. X            case 'i':   /* Implode only */
  1115. X              method = IMPLODE;  break;
  1116. X            case 'j':   /* Junk directory names */
  1117. X              pathput = 0;  break;
  1118. X            case 'k':   /* Make entries using DOS names (k for Katz) */
  1119. X              dosify = 2;  break;
  1120. X            case 'l':   /* Show license, version */
  1121. X              license();  break;
  1122. X            case 'm':   /* Delete files added or updated in zip file */
  1123. X              dispose = 1;  break;
  1124. X            case 'n':   /* Don't compress files with a special suffix */
  1125. X              if ((special = getenv("NOZIP")) == NULL)
  1126. X                special = ".Z:.zip:.zoo:.arc";
  1127. X#ifndef OS2
  1128. X#ifdef MSDOS
  1129. X              strupr(special);
  1130. X#endif /* MSDOS */
  1131. X#endif /* !OS2 */
  1132. X              break;
  1133. X            case 'o':   /* Set zip file time to time of latest file in it */
  1134. X              latest = 1;  break;
  1135. X            case 'p':   /* Store path with name */
  1136. X              break;            /* (do nothing as annoyance avoidance) */
  1137. X            case 'q':   /* Quiet operation */
  1138. X              noisy = 0;  break;
  1139. X            case 'r':   /* Recurse into subdirectories */
  1140. X              recurse = 1;  break;
  1141. X            case 's':   /* Shrink only */
  1142. X              method = SHRINK;  break;
  1143. X            case 't':   /* Exclude files earlier than specified date */
  1144. X              if (before)
  1145. X                err(ZE_PARMS, "can only have one -t");
  1146. X              k = 2;  break;
  1147. X            case 'u':   /* Update zip file--overwrite only if newer */
  1148. X              if (action != ADD)
  1149. X                err(ZE_PARMS, "specify just one action");
  1150. X              action = UPDATE;
  1151. X              break;
  1152. X            case 'v':   /* Mention oddities in zip file structure */
  1153. X              verbose = 1;
  1154. X              break;
  1155. X#ifdef VMS
  1156. X            case 'w':   /* Append the VMS version number */
  1157. X              vmsver = 1;  break;
  1158. X#endif /* VMS */
  1159. X            case 'x':   /* Exclude following files */
  1160. X              if (k != 4 &&
  1161. X                  (k != 3 || (action != UPDATE && action != FRESHEN)))
  1162. X                err(ZE_PARMS, "nothing to exclude (-x) from");
  1163. X              if (k == 3)       /* must be -u or -f */
  1164. X                for (z = zfiles; z != NULL; z = z->nxt)
  1165. X                  z->mark = 1;  /* mark all of them */
  1166. X              k = 5;
  1167. X              if ((r = exclude()) != ZE_OK)
  1168. X                if (r == ZE_PARMS)
  1169. X                  err(r, "cannot repeat names in zip file");
  1170. X                else
  1171. X                  err(r, "was processing list of files");
  1172. X              break;
  1173. X#ifdef S_IFLNK
  1174. X            case 'y':   /* Store symbolic links as such */
  1175. X              linkput = 1;  break;
  1176. X#endif /* !S_IFLNK */
  1177. X            case 'z':   /* Edit zip file comment */
  1178. X              zipedit = 1;  break;
  1179. X            default:
  1180. X            {
  1181. X              sprintf(errbuf, "no such option: %c", *p);
  1182. X              err(ZE_PARMS, errbuf);
  1183. X            }
  1184. X          }
  1185. X      else              /* just a dash */
  1186. X        if (k < 3)
  1187. X          err(ZE_PARMS, "zip file cannot be stdin");
  1188. X        else            /* read names from stdin */
  1189. X          while ((p = getnam(errbuf)) != NULL)
  1190. X          {
  1191. X            if ((r = PROCNAME(p)) != ZE_OK)
  1192. X              if (r == ZE_MISS)
  1193. X                warn("name not matched: ", p);
  1194. X              else
  1195. X                err(r, p);
  1196. X          }
  1197. X    else                /* not an option */
  1198. X    {
  1199. X      switch (k)
  1200. X      {
  1201. X        case 0:
  1202. X          if ((zipfile = ziptyp(argv[i])) == NULL)
  1203. X            err(ZE_MEM, "was processing arguments");
  1204. X          if ((r = readzipfile()) != ZE_OK)
  1205. X            err(r, zipfile);
  1206. X          found = NULL;
  1207. X          fnxt = &found;
  1208. X          k = 3;
  1209. X          break;
  1210. X        case 1:
  1211. X          if ((tempath = malloc(strlen(argv[i]) + 1)) == NULL)
  1212. X            err(ZE_MEM, "was processing arguments");
  1213. X          strcpy(tempath, argv[i]);
  1214. X          k = 0;
  1215. X          break;
  1216. X        case 2:
  1217. X        {
  1218. X          int yy, mm, dd;       /* results of sscanf() */
  1219. X
  1220. X          if (sscanf(argv[i], "%2d%2d%2d", &mm, &dd, &yy) != 3 ||
  1221. X              mm < 1 || mm > 12 || dd < 1 || dd > 31)
  1222. X            err(ZE_PARMS, "invalid date entered for -t option");
  1223. X          before = dostime(yy + (yy < 80 ? 2000 : 1900), mm, dd, 0, 0, 0);
  1224. X          k = 0;
  1225. X          break;
  1226. X        }
  1227. X        case 3:  case 4:  case 5:
  1228. X          if ((r = PROCNAME(argv[i])) != ZE_OK)
  1229. X            if (r == ZE_MISS)
  1230. X              warn("name not matched: ", argv[i]);
  1231. X            else
  1232. X              err(r, argv[i]);
  1233. X          if (k == 3)
  1234. X            k = 4;
  1235. X      }
  1236. X    }
  1237. X  }
  1238. X  if (k < 3)
  1239. X    exit(0);                    /* No zip file, don't complain */
  1240. X  if (k != 5)                   /* Clean up selections */
  1241. X  {
  1242. X    if (k == 3 && (action == UPDATE || action == FRESHEN))
  1243. X      for (z = zfiles; z != NULL; z = z->nxt)
  1244. X        z->mark = 1;                    /* if -u or -f with no args, do all */
  1245. X    if ((r = exclude()) != ZE_OK)       /* remove duplicates in found list */
  1246. X      if (r == ZE_PARMS)
  1247. X        err(r, "cannot repeat names in zip file");
  1248. X      else
  1249. X        err(r, "was processing list of files");
  1250. X  }
  1251. X  if (zcount)
  1252. X    free((voidp *)zsort);
  1253. X
  1254. X
  1255. X  /* Check option combinations */
  1256. X  if (action == DELETE && (method != BEST || dispose || recurse ||
  1257. X      dosify || key != NULL || comadd || zipedit))
  1258. X    err(ZE_PARMS, "invalid option(s) used with -d");
  1259. X  if (linkput && dosify)
  1260. X    err(ZE_PARMS, "can't use -y with -k");
  1261. X
  1262. X  /* If -b not specified, make temporary path the same as the zip file */
  1263. X#ifdef MSDOS
  1264. X  if (tempath == NULL && ((p = strrchr(zipfile, '/')) != NULL ||
  1265. X                          (p = strrchr(zipfile, '\\')) != NULL ||
  1266. X                          (p = strrchr(zipfile, ':')) != NULL))
  1267. X  {
  1268. X    if (*p == ':')
  1269. X      p++;
  1270. X#else /* !MSDOS */
  1271. X  if (tempath == NULL && (p = strrchr(zipfile, '/')) != NULL)
  1272. X  {
  1273. X#endif /* ?MSDOS */
  1274. X    if ((tempath = malloc((int)(p - zipfile) + 1)) == NULL)
  1275. X      err(ZE_MEM, "was processing arguments");
  1276. X    r = *p;  *p = 0;
  1277. X    strcpy(tempath, zipfile);
  1278. X    *p = (char)r;
  1279. X  }
  1280. X
  1281. X  /* If under MSDOS, force entries to look like made by PKZIP */
  1282. X#ifndef OS2
  1283. X#ifdef MSDOS
  1284. X  dosify = 1;
  1285. X#endif /* MSDOS */
  1286. X#endif /* !OS2 */
  1287. X
  1288. X
  1289. X  /* For each marked entry, if not deleting, check if it exists, and if
  1290. X     updating or freshening, compare date with entry in old zip file.
  1291. X     Unmark if it doesn't exist or is too old, else update marked count. */
  1292. X  diag("stating marked entries");
  1293. X  k = 0;                        /* Initialize marked count */
  1294. X  for (z = zfiles; z != NULL; z = z->nxt)
  1295. X    if (z->mark)
  1296. X      if (action != DELETE &&
  1297. X                ((t = filetime(z->name, (ulg *)NULL, (long *)NULL)) == 0 ||
  1298. X                 t < before ||
  1299. X                 ((action == UPDATE || action == FRESHEN) && t <= z->tim)))
  1300. X      {
  1301. X        z->mark = 0;
  1302. X        z->trash = t && t >= before;    /* delete if -um or -fm */
  1303. X        if (verbose)
  1304. X          printf("zip diagnostic: %s %s\n", z->name,
  1305. X                 z->trash ? "up to date" : "missing or early");
  1306. X      }
  1307. X      else
  1308. X        k++;
  1309. X
  1310. X
  1311. X  /* Remove entries from found list that do not exist or are too old */
  1312. X  diag("stating new entries");
  1313. X  for (f = found; f != NULL;)
  1314. X    if (action == DELETE || action == FRESHEN ||
  1315. X        (t = filetime(f->name, (ulg *)NULL, (long *)NULL)) == 0 ||
  1316. X        t < before || strcmp(f->name, zipfile) == 0)
  1317. X      f = fexpel(f);
  1318. X    else
  1319. X      f = f->nxt;
  1320. X
  1321. X
  1322. X  /* Make sure there's something left to do */
  1323. X  if (k == 0 && found == NULL && !(zfiles != NULL && (latest || zipedit)))
  1324. X    if (action == UPDATE || action == FRESHEN)
  1325. X      leave(ZE_OK);
  1326. X    else
  1327. X      err(ZE_NONE, zipfile);
  1328. X  d = (d && k == 0 && zfiles != NULL);  /* d true if just appending */
  1329. X
  1330. X
  1331. X  /* Before we get carried away, make sure zip file is writeable */
  1332. X  if ((x = fopen(zipfile, zfiles == NULL ? FOPW : FOPM)) == NULL)
  1333. X    err(ZE_CREAT, zipfile);
  1334. X  fclose(x);
  1335. X  a = getfileattr(zipfile);
  1336. X  if (zfiles == NULL)
  1337. X    destroy(zipfile);
  1338. X
  1339. X
  1340. X  /* Open zip file and temporary output file */
  1341. X  diag("opening zip file and creating temporary zip file");
  1342. X  x = NULL;
  1343. X  if (d)
  1344. X  {
  1345. X    if ((y = fopen(zipfile, FOPM)) == NULL)
  1346. X      err(ZE_NAME, zipfile);
  1347. X    tempzip = zipfile;
  1348. X    tempzf = y;
  1349. X#ifdef MSDOS
  1350. X    {
  1351. X      char *zipbuf;
  1352. X
  1353. X      zipbuf = (char *)malloc(BSZ);
  1354. X      if (zipbuf == NULL)
  1355. X        err(ZE_MEM, tempzip);
  1356. X      setbuf(y, zipbuf);
  1357. X    }
  1358. X#endif /* MSDOS */
  1359. X    if (fseek(y, cenbeg, SEEK_SET))
  1360. X      err(ferror(y) ? ZE_READ : ZE_EOF, zipfile);
  1361. X  }
  1362. X  else
  1363. X  {
  1364. X    if (zfiles != NULL && (x = fopen(zipfile, FOPR)) == NULL)
  1365. X      err(ZE_NAME, zipfile);
  1366. X    if ((tempzip = tempname('Z')) == NULL)
  1367. X      err(ZE_MEM, tempzip);
  1368. X    if ((tempzf = y = fopen(tempzip, FOPW)) == NULL)
  1369. X      err(ZE_TEMP, tempzip);
  1370. X    if (zipbeg && (r = fcopy(x, y, zipbeg)) != ZE_OK)
  1371. X      err(r, r == ZE_TEMP ? tempzip : zipfile);
  1372. X  }
  1373. X  o = 0;                                /* no ZE_OPEN errors yet */
  1374. X
  1375. X
  1376. X  /* Process zip file, updating marked files */
  1377. X  if (zfiles != NULL)
  1378. X    diag("going through old zip file");
  1379. X  w = &zfiles;
  1380. X  while ((z = *w) != NULL)
  1381. X    if (z->mark)
  1382. X    {
  1383. X      /* if not deleting, zip it up */
  1384. X      if (action != DELETE)
  1385. X      {
  1386. X        if (noisy)
  1387. X        {
  1388. X          printf("updating %s", z->zname);
  1389. X          fflush(stdout);
  1390. X        }
  1391. X        if ((r = zipup(z, y)) != ZE_OK && r != ZE_OPEN)
  1392. X        {
  1393. X          if (noisy)
  1394. X          {
  1395. X            putchar('\n');
  1396. X            fflush(stdout);
  1397. X          }
  1398. X          sprintf(errbuf, "was zipping %s", z->name);
  1399. X          err(r, errbuf);
  1400. X        }
  1401. X        if (r == ZE_OPEN)
  1402. X        {
  1403. X          o = 1;
  1404. X          if (noisy)
  1405. X          {
  1406. X            putchar('\n');
  1407. X            fflush(stdout);
  1408. X          }
  1409. X          perror("zip warning");
  1410. X          warn("could not open for reading: ", z->name);
  1411. X          warn("will just copy entry over: ", z->zname);
  1412. X          if ((r = zipcopy(z, x, y)) != ZE_OK)
  1413. X          {
  1414. X            sprintf(errbuf, "was copying %s", z->zname);
  1415. X            err(r, errbuf);
  1416. X          }
  1417. X          z->mark = 0;
  1418. X        }
  1419. X        w = &z->nxt;
  1420. X      }
  1421. X      else
  1422. X      {
  1423. X        if (noisy)
  1424. X        {
  1425. X          printf("deleting %s\n", z->zname);
  1426. X          fflush(stdout);
  1427. X        }
  1428. X        v = z->nxt;                     /* delete entry from list */
  1429. X        free((voidp *)(z->name));
  1430. X        free((voidp *)(z->zname));
  1431. X        if (z->ext)
  1432. X          free((voidp *)(z->extra));
  1433. X        if (z->cext && z->cextra != z->extra)
  1434. X          free((voidp *)(z->cextra));
  1435. X        if (z->com)
  1436. X          free((voidp *)(z->comment));
  1437. X        farfree((voidp far *)z);
  1438. X        *w = v;
  1439. X        zcount--;
  1440. X      }
  1441. X    }
  1442. X    else
  1443. X    {
  1444. X      /* copy the original entry verbatim */
  1445. X      if (!d && (r = zipcopy(z, x, y)) != ZE_OK)
  1446. X      {
  1447. X        sprintf(errbuf, "was copying %s", z->zname);
  1448. X        err(r, errbuf);
  1449. X      }
  1450. X      w = &z->nxt;
  1451. X    }
  1452. X  if (x != NULL)
  1453. X    fclose(x);
  1454. X
  1455. X
  1456. X  /* Process the edited found list, adding them to the zip file */
  1457. X  diag("zipping up new entries, if any");
  1458. X  for (f = found; f != NULL; f = fexpel(f))
  1459. X  {
  1460. X    /* add a new zfiles entry and set the name */
  1461. X    if ((z = (struct zlist far *)farmalloc(sizeof(struct zlist))) == NULL)
  1462. X      err(ZE_MEM, "was adding files to zip file");
  1463. X    z->nxt = NULL;
  1464. X    z->name = f->name;
  1465. X    f->name = NULL;
  1466. X    z->zname = f->zname;
  1467. X    f->zname = NULL;
  1468. X    z->ext = z->cext = z->com = 0;
  1469. X    z->mark = 1;
  1470. X    /* zip it up */
  1471. X    if (noisy)
  1472. X    {
  1473. X      printf("adding %s", z->zname);
  1474. X      fflush(stdout);
  1475. X    }
  1476. X    if ((r = zipup(z, y)) != ZE_OK  && r != ZE_OPEN)
  1477. X    {
  1478. X      if (noisy)
  1479. X      {
  1480. X        putchar('\n');
  1481. X        fflush(stdout);
  1482. X      }
  1483. X      sprintf(errbuf, "was zipping %s", z->name);
  1484. X      err(r, errbuf);
  1485. X    }
  1486. X    if (r == ZE_OPEN)
  1487. X    {
  1488. X      o = 1;
  1489. X      if (noisy)
  1490. X      {
  1491. X        putchar('\n');
  1492. X        fflush(stdout);
  1493. X      }
  1494. X      perror("zip warning");
  1495. X      warn("could not open for reading: ", z->name);
  1496. X      free((voidp *)(z->name));
  1497. X      free((voidp *)(z->zname));
  1498. X      farfree((voidp far *)z);
  1499. X    }
  1500. X    else
  1501. X    {
  1502. X      *w = z;
  1503. X      w = &z->nxt;
  1504. X      zcount++;
  1505. X    }
  1506. X  }
  1507. X  if (key != NULL)
  1508. X  {
  1509. X    free((voidp *)key);
  1510. X    key = NULL;
  1511. X  }
  1512. X
  1513. X
  1514. X  /* Get one line comment for each new entry */
  1515. X  if (comadd)
  1516. X  {
  1517. X    if ((e = malloc(MAXCOM + 1)) == NULL)
  1518. X      err(ZE_MEM, "was reading comment lines");
  1519. X    for (z = zfiles; z != NULL; z = z->nxt)
  1520. X      if (z->mark)
  1521. X      {
  1522. X        if (noisy)
  1523. X          printf("Enter comment for %s:\n", z->name);
  1524. X        if (fgets(e, MAXCOM+1, stdin) != NULL)
  1525. X        {
  1526. X          if ((p = malloc((k = strlen(e))+1)) == NULL)
  1527. X          {
  1528. X            free((voidp *)e);
  1529. X            err(ZE_MEM, "was reading comment lines");
  1530. X          }
  1531. X          strcpy(p, e);
  1532. X          if (p[k-1] == '\n')
  1533. X            p[--k] = 0;
  1534. X          z->comment = p;
  1535. X          z->com = k;
  1536. X        }
  1537. X      }
  1538. X    free((voidp *)e);
  1539. X  }
  1540. X
  1541. X  /* Get multi-line comment for the zip file */
  1542. X  if (zipedit)
  1543. X  {
  1544. X    if ((e = malloc(MAXCOM + 1)) == NULL)
  1545. X      err(ZE_MEM, "was reading comment lines");
  1546. X    if (noisy && zcomlen)
  1547. X    {
  1548. X      puts("current zip file comment is:");
  1549. X      fwrite(zcomment, 1, zcomlen, stdout);
  1550. X      if (zcomment[zcomlen-1] != '\n')
  1551. X        putchar('\n');
  1552. X      free((voidp *)zcomment);
  1553. X    }
  1554. X    zcomment = malloc(1);
  1555. X    *zcomment = 0;
  1556. X    if (noisy)
  1557. X      puts("enter new zip file comment (end with .):");
  1558. X    while (fgets(e, MAXCOM+1, stdin) != NULL && strcmp(e, ".\n"))
  1559. X    {
  1560. X      if (e[(r = strlen(e)) - 1] == '\n')
  1561. X        e[--r] = 0;
  1562. X      if ((p = malloc((*zcomment ? strlen(zcomment) + 3 : 1) + r)) == NULL)
  1563. X      {
  1564. X        free((voidp *)e);
  1565. X        err(ZE_MEM, "was reading comment lines");
  1566. X      }
  1567. X      if (*zcomment)
  1568. X        strcat(strcat(strcpy(p, zcomment), "\r\n"), e);
  1569. X      else
  1570. X        strcpy(p, *e ? e : "\r\n");
  1571. X      free((voidp *)zcomment);
  1572. X      zcomment = p;
  1573. X    }
  1574. X    zcomlen = strlen(zcomment);
  1575. X    free((voidp *)e);
  1576. X  }
  1577. X
  1578. X
  1579. X  /* Write central directory and end header to temporary zip */
  1580. X  diag("writing central directory");
  1581. X  k = 0;                        /* keep count for end header */
  1582. X  if ((c = ftell(y)) == -1L)    /* get start of central */
  1583. X    err(d ? ZE_WRITE : ZE_TEMP, tempzip);
  1584. X  n = t = 0;
  1585. X  for (z = zfiles; z != NULL; z = z->nxt)
  1586. X  {
  1587. X    if ((r = putcentral(z, y)) != ZE_OK)
  1588. X      err(r, tempzip);
  1589. X    n += z->len;
  1590. X    t += z->siz;
  1591. X    k++;
  1592. X  }
  1593. X  if (k == 0)
  1594. X    warn("zip file empty", "");
  1595. X  if (verbose)
  1596. X    printf("total bytes=%lu, compressed=%lu -> %d%% savings\n",
  1597. X           n, t, percent(n, t));
  1598. X  if ((t = ftell(y)) == -1L)    /* get end of central */
  1599. X    err(d ? ZE_WRITE : ZE_TEMP, tempzip);
  1600. X  t -= c;                       /* compute length of central */
  1601. X  diag("writing end of central directory");
  1602. X  if ((r = putend(k, t, c, zcomlen, zcomment, y)) != ZE_OK)
  1603. X    err(r, tempzip);
  1604. X  tempzf = NULL;
  1605. X  if (fclose(y))
  1606. X    err(d ? ZE_WRITE : ZE_TEMP, tempzip);
  1607. X
  1608. X
  1609. X  /* Replace old zip file with new zip file, leaving only the new one */
  1610. X  if (!d)
  1611. X  {
  1612. X    diag("replacing old zip file with new zip file");
  1613. X    if ((r = replace(zipfile, tempzip)) != ZE_OK)
  1614. X    {
  1615. X      warn("new zip file left as: ", tempzip);
  1616. X      free((voidp *)tempzip);
  1617. X      tempzip = NULL;
  1618. X      err(r, "was replacing the original zip file");
  1619. X    }
  1620. X    free((voidp *)tempzip);
  1621. X  }
  1622. X  tempzip = NULL;
  1623. X  setfileattr(zipfile, a);
  1624. X
  1625. X  /* Finish up (process -o, -m, clean up).  Exit code depends on o. */
  1626. X  leave(o ? ZE_OPEN : ZE_OK);
  1627. X}
  1628. END_OF_FILE
  1629. if test 25825 -ne `wc -c <'zip.c'`; then
  1630.     echo shar: \"'zip.c'\" unpacked with wrong size!
  1631. fi
  1632. # end of 'zip.c'
  1633. fi
  1634. if test -f 'zip.doc' -a "${1}" != "-c" ; then 
  1635.   echo shar: Will not clobber existing file \"'zip.doc'\"
  1636. else
  1637. echo shar: Extracting \"'zip.doc'\" \(22854 characters\)
  1638. sed "s/^X//" >'zip.doc' <<'END_OF_FILE'
  1639. X
  1640. X
  1641. X
  1642. ZIP(1)              UNIX Programmer's Manual               ZIP(1)
  1643. X
  1644. X
  1645. X
  1646. NAME
  1647. X     zip - package and compress (archive) files
  1648. X
  1649. SYNOPSIS
  1650. X     zip [ -cdefghijklmnoqrsuwyz ] [ -b path ] [ -t mmddyy ] zip-
  1651. X     file list [ -x list ]
  1652. X
  1653. DESCRIPTION
  1654. X     Zip is a compression and file packaging utility for Unix,
  1655. X     MSDOS, OS/2, and VMS.  It is analogous to a combination of
  1656. X     tar and compress and is compatible with PKZIP (Phil Katz
  1657. X     ZIP) for MSDOS systems.
  1658. X
  1659. X     There is a companion to Zip called UnZip (of course) which
  1660. X     you should be able to find the same place you got Zip. Zip
  1661. X     and UnZip can work with files produced by PKZIP under MSDOS,
  1662. X     and PKZIP and PKUNZIP can work with files produced by Zip.
  1663. X
  1664. X     Zip puts one or more compressed files into a single "zip
  1665. X     file" along with information about the files, including the
  1666. X     name, path if requested, date and time last modified, pro-
  1667. X     tection, and check information to verify the fidelity of
  1668. X     each entry.  Zip can pack an entire directory structure in a
  1669. X     zip file with a single command.  Compression ratios of 2:1
  1670. X     to 3:1 are common for text files.  Zip has two compression
  1671. X     methods, implosion and shrinking, and automatically chooses
  1672. X     the better of the two for each file to be compressed.
  1673. X
  1674. X     Zip is useful for packaging a set of files to send to some-
  1675. X     one or for distribution; for archiving or backing up files;
  1676. X     and for saving disk space by temporarily compressing unused
  1677. X     files or directories.
  1678. X
  1679. HOW TO INSTALL ZIP
  1680. X     Zip is distributed as C source code that can be compiled on
  1681. X     a wide range of Unix machines, VAXes running VMS, and MSDOS
  1682. X     machines using Microsoft or Borland C++, and OS/2 machines
  1683. X     using Microsoft C.  You will need Unzip (under Unix, MSDOS,
  1684. X     or VMS) or PKUNZIP (under MSDOS) to unpack the distribution
  1685. X     file, zip10.zip.
  1686. X
  1687. X     First, unpack the source as follows, assuming that you have
  1688. X     zip10.zip in the current directory:
  1689. X
  1690. X          mkdir zipsrc
  1691. X          cd zipsrc
  1692. X          unzip ../zip10
  1693. X
  1694. X     This extracts all source files and documentation in the
  1695. X     directory called "zipsrc". You then do:
  1696. X
  1697. X
  1698. X          make system
  1699. X
  1700. X     where "system" is one of: bsd, bsdold, sysv, next, next10,
  1701. X     sun, hpux, dnix, cray, 3b1, zilog, aux, convex, aix, or
  1702. X     minix.  If you are using a NeXT running version 2.0 or
  1703. X     greater, then make next.  If you are using 1.0, then make
  1704. X     next10.  If you are using Sun OS 4.x, then make sun.  If you
  1705. X     are using HPUX, then make hpux.  The other special systems
  1706. X     are DNIX 5.2 or 5.3, Cray Unicos, AT&T 3B1 (also known as
  1707. X     Unix PC or PC 7300), Zilog Zeus, A/UX, Convex, AIX, and
  1708. X     MINIX.  Otherwise, if you are using BSD Unix, try bsd.  If
  1709. X     the linker cannot find _memset or _memcpy, try bsdold.  If
  1710. X     you are using System V Unix or SCO Unix, try sysv.  Also use
  1711. X     sysv on a Silicon Graphics (SGI) machine.  You can also
  1712. X     cross-compile Zip for MSDOS under SCO 386 Unix using "make
  1713. X     scodos".
  1714. X
  1715. X     If none of these compiles, links, and functions properly on
  1716. X     your Unix system, see the section BUGS below for how to get
  1717. X     help.
  1718. X
  1719. X     If the appropriate system was selected, then the executable
  1720. X     "zip" will be created.  You can move the executable "zip" to
  1721. X     an appropriate directory in the search path using a command
  1722. X     like:
  1723. X
  1724. X          mv zip ~/bin
  1725. X
  1726. X     or
  1727. X
  1728. X          mv zip /usr/local/bin
  1729. X
  1730. X     You can use the command "set" to see the current search
  1731. X     path.  If you are using the C-Shell (csh), enter the com-
  1732. X     mand:
  1733. X
  1734. X          rehash
  1735. X
  1736. X     so csh can find the new command in the path.  You are now
  1737. X     ready to use Zip.
  1738. X
  1739. X     You can also move the manual page (the raw form of what
  1740. X     you're reading) to where the Unix man command can find it
  1741. X     (assuming you have the necessary privileges):
  1742. X
  1743. X          mv zip.1 /usr/man/man1
  1744. X
  1745. X     You can get rid of the now unnecessary source and object
  1746. X     files with:
  1747. X
  1748. X          cd ..
  1749. X          rm -r zipsrc
  1750. X
  1751. X     This will remove the directory zip and its contents created
  1752. X     by unzip.  You should keep the zip10.zip file around though,
  1753. X     in case you need to build it again or want to give it to a
  1754. X     colleague.
  1755. X
  1756. X     The steps for installation under MSDOS, OS/2, and VMS are
  1757. X     similar to the above: first unzip the distribution files
  1758. X     into their own directory.  Then under MSDOS do one of:
  1759. X
  1760. X          make makefile.msc
  1761. X          make -fmakefile.bor
  1762. X
  1763. X
  1764. X     for Microsoft or Borland C++, respectively.  Under OS/2:
  1765. X
  1766. X          nmake -f makefile.os2
  1767. X
  1768. X     for Microsoft C 6.00.  Under VAX VMS:
  1769. X
  1770. X
  1771. X          @makevms
  1772. X
  1773. X     The installation process will also compile and link several
  1774. X     other utilities.  They are zipcloak for encrypting and
  1775. X     decrypting zip files, zipnote for editing zip file comments,
  1776. X     zipsplit for splitting a zip file into several zip files,
  1777. X     and ship for sending zip files or any other binary file via
  1778. X     electronic mail.  For command help on any of the zip* utili-
  1779. X     ties, simply enter the name with no arguments.  For help
  1780. X     with ship, enter "ship -h".
  1781. X
  1782. HOW TO USE ZIP
  1783. X     The simplest use of Zip is as follows:
  1784. X
  1785. X          zip stuff *
  1786. X
  1787. X     This will create the file "stuff.zip" (assuming it does not
  1788. X     exist) and put all the files in the current directory in
  1789. X     stuff.zip in a compressed form.  The .zip suffix is added
  1790. X     automatically, unless that file name given contains a dot
  1791. X     already.  This allows specifying suffixes other than ".zip".
  1792. X
  1793. X     Because of the way the shell does filename substitution,
  1794. X     files that start with a "." are not included.  To include
  1795. X     those as well, you can:
  1796. X
  1797. X          zip stuff .* *
  1798. X
  1799. X     Even this will not include any subdirectories that are in
  1800. X     the current directory.  To zip up an entire directory, the
  1801. X     command:
  1802. X
  1803. X          zip -r foo foo
  1804. X
  1805. X     will create the file "foo.zip" containing all the files and
  1806. X     directories in the directory "foo" that is in the current
  1807. X     directory.  The "r" option means recurse through the direc-
  1808. X     tory structure.  In this case, all the files and directories
  1809. X     in foo are zipped, including the ones that start with a ".",
  1810. X     since the recursion does not use the shell's file-name sub-
  1811. X     stitution.  You should not use -r with the name ".*", since
  1812. X     that matches ".." which will attempt to zip up the parent
  1813. X     directory--probably not what was intended.
  1814. X
  1815. X     You may want to make a zip file that contains the files in
  1816. X     foo, but not record the directory name, foo.  You can use
  1817. X     the -j (junk path) option to leave off the path:
  1818. X
  1819. X          zip -j foo foo/*
  1820. X
  1821. X     The -y option (only under Unix) will store symbolic links as
  1822. X     such in the zip file, instead of compressing and storing the
  1823. X     file referred to in the link.
  1824. X
  1825. X     You might be zipping to save disk space, in which case you
  1826. X     could:
  1827. X
  1828. X          zip -rm foo foo
  1829. X
  1830. X     where the "m" option means "move".  This will delete foo and
  1831. X     its contents after making foo.zip.  No deletions will be
  1832. X     done until the zip has completed with no errors.  This
  1833. X     option is obviously more dangerous and should be used with
  1834. X     care.
  1835. X
  1836. X
  1837. X     If the zip file already exists, these commands will replace
  1838. X     existing or add new entries to the zip file.  For example,
  1839. X     if you were really short on disk space, you might not have
  1840. X     enough room simultaneously to hold the directory foo and the
  1841. X     compressed foo.zip.  In this case, you could do it in steps.
  1842. X     If foo contained the subdirectories tom, dick, and harry,
  1843. X     then you could:
  1844. X
  1845. X          zip -rm foo foo/tom
  1846. X          zip -rm foo foo/dick
  1847. X          zip -rm foo foo/harry
  1848. X
  1849. X     where the first command would create foo.zip, and the next
  1850. X     two would add to it.  At the completion of each zip command,
  1851. X     the directory just zipped would be deleted, making room in
  1852. X     which the next Zip command could work.
  1853. X
  1854. MODIFYING EXISTING ZIP FILES
  1855. X     When given the name of an existing zip file with the above
  1856. X     commands, Zip will replace identically named entries in the
  1857. X     Zip file or add entries for new names.  For example, if
  1858. X     foo.zip exists and contains foo/file1 and foo/file2, and the
  1859. X     directory foo contains the files foo/file1 and foo/file3,
  1860. X     then:
  1861. X
  1862. X          zip -r foo foo
  1863. X
  1864. X     will replace foo/file1 in foo.zip and add foo/file3 to
  1865. X     foo.zip.  After this, foo.zip contains foo/file1, foo/file2,
  1866. X     and foo/file3, with foo/file2 unchanged from before.
  1867. X
  1868. X     When changing an existing zip file, Zip will write a tem-
  1869. X     porary file with the new contents, and only replace the old
  1870. X     one when the zip has completed with no errors.  Also, the
  1871. X     two methods, shrink and implode, create temporary files that
  1872. X     are deleted after each file is zipped.  You can use the -b
  1873. X     option to specify a different path (usually a different dev-
  1874. X     ice) to put the temporary files in.  For example:
  1875. X
  1876. X          zip -b /tmp stuff *
  1877. X
  1878. X     will put the temporary zip file and the temporary compres-
  1879. X     sion files in the directory "/tmp", copying over stuff.zip
  1880. X     in the current directory when done.
  1881. X
  1882. X     If you are only adding entries to a zip file, not replacing,
  1883. X     and the -g option is given, then Zip grows (appends to) the
  1884. X     file instead of copying it.  The danger of this is that if
  1885. X     the operation fails, the original zip file is corrupted and
  1886. X     lost.
  1887. X
  1888. X     There are two other ways to change or add entries in a zip
  1889. X     file that are restrictions of simple addition or replace-
  1890. X     ment.  The first is -u (update) which will add new entries
  1891. X     to the zip file as before but will replace existing entries
  1892. X     only if the modified date of the file is more recent than
  1893. X     the date recorded for that name in the zip file.  For exam-
  1894. X     ple:
  1895. X
  1896. X          zip -u stuff *
  1897. X
  1898. X     will add any new files in the current directory, and update
  1899. X     any changed files in the zip file stuff.zip.  Note that Zip
  1900. X     will not try to pack stuff.zip into itself when you do this.
  1901. X     Zip will always exclude the zip file from the files on which
  1902. X     to be operated.
  1903. X
  1904. X     The second restriction is -f (freshen) which, like update,
  1905. X     will only replace entries with newer files; unlike update,
  1906. X     will not add files that are not already in the zip file.
  1907. X     For this option, you may want to simply freshen all of the
  1908. X     files that are in the specified zip file.  To do this you
  1909. X     would simply:
  1910. X
  1911. X          zip -f foo
  1912. X
  1913. X     Note that the -f option with no arguments freshens all the
  1914. X     entries in the zip file.  The same is true of -u, and hence
  1915. X     "zip -u foo" and "zip -f foo" both do the same thing.
  1916. X
  1917. X     This command should be run from the same directory from
  1918. X     which the original zip command was run, since paths stored
  1919. X     in zip files are always relative.
  1920. X
  1921. X     Another restriction that can be used with adding, updating,
  1922. X     or freshening is -t (time), which will not operate on files
  1923. X     modified earlier than the specified date.  For example:
  1924. X
  1925. X          zip -rt 120791 infamy foo
  1926. X
  1927. X     will add all the files in foo and its subdirectories that
  1928. X     were last modified on December 7, 1991, or later to the zip
  1929. X     file infamy.zip.
  1930. X
  1931. X     Also, files can be explicitly excluded using the -x option:
  1932. X
  1933. X          zip -r foo foo -x \*.o
  1934. X
  1935. X     which will zip up the contents of foo into foo.zip but
  1936. X     exclude all the files that end in ".o".  Here the backslash
  1937. X     causes Zip to match file names that were found when foo was
  1938. X     searched.
  1939. X
  1940. X     The last operation is -d (delete) which will remove entries
  1941. X     from a zip file.  An example might be:
  1942. X
  1943. X          zip -d foo foo/tom/junk foo/harry/\* \*.o
  1944. X
  1945. X     which will remove the entry foo/tom/junk, all of the files
  1946. X     that start with "foo/harry/", and all of the files that end
  1947. X     with ".o" (in any path).  Note that once again, the shell
  1948. X     expansion has been inhibited with backslashes, so that Zip
  1949. X     can see the asterisks. Zip can then match on the contents of
  1950. X     the zip file instead of the contents of the current direc-
  1951. X     tory.
  1952. X
  1953. X     Under MSDOS, -d is case sensitive when it matches names in
  1954. X     the zip file.  This allows deleting names that were zipped
  1955. X     on other systems, but requires that the names be entered in
  1956. X     upper case if they were zipped on an MSDOS system, so that
  1957. X     the names can be found in the zip file and deleted.
  1958. X
  1959. MORE OPTIONS
  1960. X     As mentioned before, Zip will use the best of two methods:
  1961. X     shrink or implode.  Usually implode is better, but sometimes
  1962. X     shrink is better, especially for smaller files.  Sometimes
  1963. X     neither method produces a packed version smaller than the
  1964. X     original file, in which case it is stored in the zip file
  1965. X     with no compression (called the "store" method).
  1966. X
  1967. X     The option -s (shrink) will force Zip always to use shrink
  1968. X     or store, and the -i (implode) option forces Zip to use
  1969. X     implode or store.  Shrinking is faster than imploding, and
  1970. X     so -s might be used when speed is more important than
  1971. X     optimal compression.  Implode only (-i) might be used when
  1972. X     the unzipper for which the zip file is destined can only
  1973. X     handle implosion.  An example of this is the PKSFXjr program
  1974. X     that comes with PKZIP.  Also, -i is slightly faster than
  1975. X     imploding and shrinking at the same time.  For example:
  1976. X
  1977. X          zip -rs foo foo
  1978. X
  1979. X     will zip up the directory foo into foo.zip using only shrink
  1980. X     or store.  The speed of implosion can also be controlled
  1981. X     with options -0 (fastest method but less compression) to -9
  1982. X     (best compression but slower). The default value is -5. For
  1983. X     example:
  1984. X
  1985. X          zip -r0 foo foo
  1986. X
  1987. X     In nearly all cases, a file that is already compressed can-
  1988. X     not be compressed further by Zip, or if it can, the effect
  1989. X     is minimal.  The -n option prevents Zip from trying to
  1990. X     compress files that have the suffixes: .Z, .zip, .zoo, or
  1991. X     .arc.  Such files are simply stored (0% compression) in the
  1992. X     output zip file, so that Zip doesn't waste its time trying
  1993. X     to compress them.  If the environment variable NOZIP is set,
  1994. X     then the suffixes listed there are used instead of the
  1995. X     default list.  The suffixes are separated by either colons
  1996. X     or semicolons.  For example, in Unix csh:
  1997. X
  1998. X          setenv NOZIP .Z:.zip:.tiff:.gif:.snd
  1999. X          zip -rn foo foo
  2000. X
  2001. X     will put everything in foo into foo.zip, but will store any
  2002. X     files that end in .Z, .zip, .tiff, .gif, or .snd without
  2003. X     trying to compress them.  (Image and sound files often have
  2004. X     their own specialized compression methods.)  If the environ-
  2005. X     ment variable NOZIP exists but is empty or contains just a
  2006. X     colon or semicolon, then zip -n will store all the entries
  2007. X     and do no compression.
  2008. X
  2009. X     Under Unix and under OS/2 (if files from a HPFS are stored),
  2010. X     Zip will store the full path (relative to the current path)
  2011. X     and name of the file (or just the name if -j is specified)
  2012. X     in the zip file along with the Unix attributes, and it will
  2013. X     mark the entry as made under Unix.  If the zip file is
  2014. X     intended for PKUNZIP under MSDOS, then the -k (Katz) option
  2015. X     should be used to attempt to convert the names and paths to
  2016. X     conform to MSDOS, store only the MSDOS attribute (just the
  2017. X     user write attribute from Unix), and mark the entry as made
  2018. X     under MSDOS (even though it wasn't).
  2019. X
  2020. X     The -o (older) option will set the "last modified" time of
  2021. X     the zip file to the latest "last modified" time of the
  2022. X     entries in the zip file.  This can be used without any other
  2023. X     operations, if desired.  For example:
  2024. X
  2025. X          zip -o foo
  2026. X
  2027. X
  2028. X     will change the last modified time of foo.zip to the latest
  2029. X     time of the entries in foo.zip.
  2030. X
  2031. X     The -e and -c options operate on all files updated or added
  2032. X     to the zip file.  Encryption (-e) will prompt for a password
  2033. X     on the terminal and will not echo the password as it is
  2034. X     typed (if stderr is not a TTY, Zip will exit with an error).
  2035. X     New zip entries will be encrypted using that password.  For
  2036. X     added peace of mind, you can use -ee, which will prompt for
  2037. X     the password twice, checking that the two are the same
  2038. X     before using it.
  2039. X
  2040. X     One-line comments can be added for each file with the -c
  2041. X     option.  The zip file operations (adding or updating) will
  2042. X     be done first, and you will then be prompted for a one-line
  2043. X     comment for each file.  You can then enter the comment fol-
  2044. X     lowed by return, or just return for no comment.
  2045. X
  2046. X     The -z option will prompt you for a multi-line comment for
  2047. X     the entire zip file.  This option can be used by itself, or
  2048. X     in combination with other options.  The comment is ended by
  2049. X     a line containing just a period, or an end of file condition
  2050. X     (^D on Unix, ^Z on MSDOS, OS/2, and VAX/VMS).  Since -z
  2051. X     reads the lines from stdin, you can simply take the comment
  2052. X     from a file:
  2053. X
  2054. X          zip -z foo < foowhat
  2055. X
  2056. X     The -q (quiet) option eliminates the informational messages
  2057. X     and comment prompts while Zip is operating.  This might be
  2058. X     used in shell scripts, for example, or if the zip operation
  2059. X     is being performed as a background task ("zip -q foo *.c
  2060. X     &").
  2061. X
  2062. X     Zip can take a list of file names to operate on from stdin
  2063. X     using the - option.  In Unix, this option can be used with
  2064. X     the find command to extend greatly the functionality of Zip.
  2065. X     For example, to zip up all the C source files in the current
  2066. X     directory and its subdirectories, you can:
  2067. X
  2068. X          find . -type f -name "*.[ch]" -print | zip source -
  2069. X
  2070. X     Note that the pattern must be quoted to keep the shell from
  2071. X     expanding it.
  2072. X
  2073. X     Under VMS only, the -w option will append the version number
  2074. X     of the files to the name and zip up multiple versions of
  2075. X     files.  Without -w, Zip will only use the most recent ver-
  2076. X     sion of the specified file(s).
  2077. X
  2078. X     If Zip is run with no arguments or with the -h option, the
  2079. X     license and the command-argument and option help is shown.
  2080. X     The -l option just shows the license.
  2081. X
  2082. ABOUT PATTERN MATCHING
  2083. X     (Note: this section applies to Unix.  Watch this space for
  2084. X     details on MSDOS and VMS operation.)
  2085. X
  2086. X     The Unix shell (sh or csh) does filename substitution on
  2087. X     command arguments.  The special characters are ?, which
  2088. X     matches any single character; * which matches any number of
  2089. X     characters (including none); and [] which matches any char-
  2090. X     acter in the range inside the brackets (like [a-f] or
  2091. X     [0-9]).  When these characters are encountered (and not
  2092. X     escaped with a backslash or quotes), the shell will look for
  2093. X     files relative to the current path that match the pattern,
  2094. X     and replace the argument with a list of the names that
  2095. X     matched.
  2096. X
  2097. X     Zip can do the same matching on names that are in the zip
  2098. X     file being modified or, in the case of the -x (exclude)
  2099. X     option, on the list of files to be operated on, by using
  2100. X     backslashes or quotes to tell the shell not to do the name
  2101. X     expansion.  In general, when Zip encounters a name in the
  2102. X     list of files to do, it first looks for the name in the file
  2103. X     system.  If it finds it, it then adds it to the list of
  2104. X     files to do.  If it does not find it, it will look for the
  2105. X     name in the zip file being modified (if it exists), using
  2106. X     the pattern matching characters above, if any.  For each
  2107. X     match, it will add that name to the list of files to do.
  2108. X     After -x (exclude), the names are removed from the to-do
  2109. X     list instead of added.
  2110. X
  2111. X     The pattern matching includes the path, and so patterns like
  2112. X     \*.o match names that end in ".o", no matter what the path
  2113. X     prefix is.  Note that the backslash must precede every spe-
  2114. X     cial character (i.e. ?*[]), or the entire argument must be
  2115. X     enclosed in double quotes ("").
  2116. X
  2117. X     In general, using backslash to make Zip do the pattern
  2118. X     matching is used with the -f (freshen) and -d (delete)
  2119. X     options, and sometimes after the -x (exclude) option when
  2120. X     used with any operation (add, -u, -f, or -d).  Zip will
  2121. X     never use pattern matching to search the file system.  If
  2122. X     Zip has recursed into a directory, all files (and all direc-
  2123. X     tories) in there are fair game.
  2124. X
  2125. COPYRIGHT
  2126. X     Copyright (C) 1990,1991 Mark Adler, Richard B. Wales, and
  2127. X     Jean-loup Gailly.  Permission is granted to any individual
  2128. X     or institution to use, copy, or redistribute this software
  2129. X     so long as all of the original files are included unmodi-
  2130. X     fied, that it is not sold for profit, and that this copy-
  2131. X     right notice is retained.
  2132. X
  2133. ACKNOWLEDGEMENTS
  2134. X     Thanks to R. P. Byrne for his Shrink.Pas program which
  2135. X     inspired this project, and from which the shrink algorithm
  2136. X     was stolen; to Phil Katz for making the zip file format,
  2137. X     compression format, and .ZIP filename extension all public
  2138. X     domain; to Keith Petersen for providing a mailing list and
  2139. X     ftp site for the INFO-ZIP group to use; and most impor-
  2140. X     tantly, to the INFO-ZIP group itself (listed in the file
  2141. X     infozip.who) without whose tireless testing and bug-fixing
  2142. X     efforts a portable Zip would not have been possible.
  2143. X     Finally we should thank (blame) the INFO-ZIP moderator,
  2144. X     David Kirschbaum for getting us into this mess in the first
  2145. X     place.
  2146. X
  2147. SEE ALSO
  2148. X     unzip(1), tar(1), compress(1)
  2149. X
  2150. BUGS
  2151. X     Versions of PKUNZIP before 1.1 have a bug that on rare occa-
  2152. X     sions will prevent it from unzipping files produced by Zip
  2153. X     or PKZIP 1.1.  If you experience such problems, we recommend
  2154. X     that you get PKUNZIP 1.1 or the portable Unzip, neither of
  2155. X     which have this problem.
  2156. X
  2157. X     Under MSDOS, Zip will find hidden and system files, but not
  2158. X     set the attributes appropriately in the zip file so that
  2159. X     Unzip can restore them.  This will be fixed in the next
  2160. X     version.
  2161. X
  2162. X     Under VMS, not all of the odd file formats are treated prop-
  2163. X     erly.  Only stream-LF format zip files are expected to work
  2164. X     with Zip.  Others can be converted using Rahul Dhesi's BILF
  2165. X     program.  The next version of Zip will handle some of the
  2166. X     conversion internally.
  2167. X
  2168. X     LIKE ANYTHING ELSE THAT'S FREE, ZIP AND ITS ASSOCIATED UTIL-
  2169. X     ITIES ARE PROVIDED AS IS AND COME WITH NO WARRANTY OF ANY
  2170. X     KIND, EITHER EXPRESSED OR IMPLIED. IN NO EVENT WILL THE
  2171. X     COPYRIGHT HOLDERS BE LIABLE FOR ANY DAMAGES RESULTING FROM
  2172. X     THE USE OF THIS SOFTWARE.
  2173. X
  2174. X     That having been said, please send any problems or comments
  2175. X     via email to the Internet address zip-bugs@cs.ucla.edu.  For
  2176. X     bug reports, please include the version of Zip, the make
  2177. X     options you used to compile it, the machine and operating
  2178. X     system you are using, and as much additional information as
  2179. X     possible.  Thank you for your support.
  2180. X
  2181. END_OF_FILE
  2182. if test 22854 -ne `wc -c <'zip.doc'`; then
  2183.     echo shar: \"'zip.doc'\" unpacked with wrong size!
  2184. fi
  2185. # end of 'zip.doc'
  2186. fi
  2187. echo shar: End of archive 4 \(of 7\).
  2188. cp /dev/null ark4isdone
  2189. MISSING=""
  2190. for I in 1 2 3 4 5 6 7 ; do
  2191.     if test ! -f ark${I}isdone ; then
  2192.     MISSING="${MISSING} ${I}"
  2193.     fi
  2194. done
  2195. if test "${MISSING}" = "" ; then
  2196.     echo You have unpacked all 7 archives.
  2197.     rm -f ark[1-9]isdone
  2198. else
  2199.     echo You still need to unpack the following archives:
  2200.     echo "        " ${MISSING}
  2201. fi
  2202. ##  End of shell archive.
  2203. exit 0
  2204.